using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Globalization;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sedna.Core.Controller;
using Sedna.Core.Controller.Configurations;
using Sedna.Core.Controls.Support;
using Sedna.Core.Controls.Support.Javascript;
using Sedna.Core.DataBinding;
using Sedna.Core.Tiles.Contents;
using DataBinder=Sedna.Core.DataBinding.DataBinder;

namespace Sedna.Core.Controls
{
    [ValidationProperty("SelectedCount")]
    public class ListBox : System.Web.UI.WebControls.ListBox, IContent, IConfigurationAware, IClientScriptingControl, IControlDataLoadAware, IListControl, IControlBindingAware
    {

        #region Fields
        private bool clientScriptingEnabled;
        private string clientVariable;
        private string javascriptClass;
        private string configurationId;
        private string contentPlaceHolderId;
        protected IDataBinder binder;
        private IMultiSelectListController controller;

        private IListBoxConfiguration configuration;

        #endregion

        #region Properties
        
        public string ContentPlaceHolderId
        {
            get { return contentPlaceHolderId; }
            set { contentPlaceHolderId = value; }
        }

        public IConfiguration Configuration
        {
            get { return configuration; }
            set { configuration = (IListBoxConfiguration)value; }
        }

        public string ConfigurationId
        {
            get { return configurationId; }
            set { configurationId = value; }
        }

        public string ClientVariable
        {
            get { return clientVariable; }
            set { clientVariable = value; }
        }

        public bool ClientScriptingEnabled
        {
            get { return clientScriptingEnabled; }
            set { clientScriptingEnabled = value; }
        }

        public IMultiSelectListController Controller
        {
            get { return controller; }
            set
            {
                if (value == null && controller != null)
                {
                    controller.OnControlChanged -= new ControlHandler(Controller_OnControlChanged);

                    if (controller.Control == this)
                        controller.Control = null;
                }

                controller = value;

                if (controller != null)
                {
                    controller.Control = this;
                    controller.OnControlChanged += new ControlHandler(Controller_OnControlChanged);
                }
            }
        }

        public string SelectedText
        {
            get { return (SelectedItem == null) ? null : SelectedItem.Text; }
        }

        public IList<ListItem> SelectedItems
        {
            get
            {
                IList<ListItem> list = new List<ListItem>();

                foreach (ListItem item in Items)
                {
                    if (item.Selected)
                        list.Add(item);
                }

                return list;
            }
        }

        public IList<String> SelectedValues
        {
            get
            {
                IList<String> list = new List<String>();

                foreach (ListItem item in SelectedItems)
                {
                    list.Add(item.Value);
                }

                return list;
            }
        }

        public int SelectedCount
        {
            get { return SelectedItems.Count; }
        }

        #endregion

        #region Javascript support

        public string JavascriptClass
        {
            get { return javascriptClass; }
            set { javascriptClass = value; }
        }

        public string NodeName
        {
            get { return ClientID; }
        }


        private void RenderScriptAttributes(HtmlTextWriter writer)
        {
            foreach (DictionaryEntry entry in BuildScriptParameters())
            {
                writer.AddAttribute("sedna:" + (string)entry.Key, (string)entry.Value);
            }
        }

        private void DeclareScriptVariable()
        {
            ControlUtil.EnsureIdAttribute(this);
            JavascriptController.Current.DeclareClass(JavascriptClass);
            if (ClientVariable != null)
            {
                JavascriptController.Current.DeclareObjectByNode(ClientVariable, JavascriptClass, NodeName);
            }
        }

        protected virtual IDictionary BuildScriptParameters()
        {
            IDictionary parameters = new ListDictionary();
            parameters.Add("jsClass", JavascriptClass);
            parameters.Add("id", ClientID);
            parameters.Add("submitState", this.SelectedItem != null ? this.SelectedItem.Text : "");
            return parameters;
        }

        #endregion        

        # region Events

        public event BindingHandler BeforeBinding;
        public event BindingHandler AfterBinding;
        public event BindingHandler BeforeUnbinding;
        public event BindingHandler AfterUnbinding;

        # endregion

        public ListBox()
            : base()
        {
            javascriptClass = "ListBox";
            CssClass = "inputsmall";
            Width = Unit.Pixel(120);
        }

        public ListBox(IConfiguration configuration)
            : this()
        {
            Configuration = configuration;
            configurationId = configuration.ObjectName;
            
            BuildControlFromConfiguration();
        }

        private void BuildControlFromConfiguration()
        {
            if (configuration == null)
            {
                return;
            }

            ID = configuration.Id;
            
            IClientScriptingConfiguration clientScriptingConfiguration = Configuration as IClientScriptingConfiguration;
            
            if (clientScriptingConfiguration != null)
            {
                javascriptClass = clientScriptingConfiguration.JavascriptClass;
                ClientVariable = clientScriptingConfiguration.ClientVariable;
                ClientScriptingEnabled = clientScriptingConfiguration.ClientScriptingEnabled;
            }

            SelectionMode = configuration.IsMultipleSelect ? ListSelectionMode.Multiple : ListSelectionMode.Single;

            if (!configuration.Width.IsEmpty)
            {
                Width = configuration.Width;
            }

            Rows = configuration.Rows;
            SelectionMode = (configuration.IsMultipleSelect) ? ListSelectionMode.Multiple : ListSelectionMode.Single;

            Controller = configuration.Controller != null ? (IMultiSelectListController)configuration.Controller.Clone() : null;
        }

        public void UnselectAll()
        {
            foreach (ListItem item in SelectedItems)
            {
                item.Selected = false;
            }
        }

        void Controller_OnControlChanged(Control control)
        {
            if (control != this)
                Controller = null;
        }

        protected override void OnInit(EventArgs e)
        {
            if (configuration == null)
            {
                Configuration = (IConfiguration)SpringContextFactory.GetContext().GetObject(configurationId);
                BuildControlFromConfiguration();
            }

            ControlUtil.SetupClientAttributes(this, configuration);
            binder = new DataBinder(this, configuration, ExecutionContext.GetCurrent(ClientID));
            base.OnInit(e);
        }

        public void OnDataLoad()
        {
            IDictionary executionContext = ExecutionContext.GetCurrent(ClientID);
            ControlUtil.ExecuteOnDataLoad(executionContext, Context, Configuration, this.ClientID);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            IExecutionContext executionContext = ExecutionContext.GetCurrent(ClientID);
            Unbind();
            ControlUtil.ExecuteOnLoad(executionContext, Context, Configuration, this.ClientID);

            if (Configuration.VisibleBindPath != null)
            {
                this.Visible = true; // To reach the OnPreRender method and analyze visibility in the Bind method
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            IDictionary executionContext = ExecutionContext.GetCurrent(ClientID);
            ControlUtil.ExecuteOnPreRender(executionContext, Context, Configuration, this.ClientID);
            Bind();
            if (ClientScriptingEnabled)
            {
                //				EnableClientScripting();
                DeclareScriptVariable();
            }

        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            RenderScriptAttributes(writer);
        }

        private void BindDataSource()
        {
            # region Setting data fields

            object dataTextField = configuration.DataTextField;

            if (String.IsNullOrEmpty(configuration.DataTextField) && !binder.GetPropertyValue("DataTextField", out dataTextField))
            {
                dataTextField = "Text";
            }

            object dataValueField = configuration.DataValueField;

            if (String.IsNullOrEmpty(configuration.DataValueField) && !binder.GetPropertyValue("DataValueField", out dataValueField))
            {
                dataValueField = "Value";
            }

            DataTextField = dataTextField.ToString();
            DataValueField = dataValueField.ToString();

            # endregion
                                   
            
            /* Neither of sort order, sort expression, virtual item count and etc. must save by grid instance,
             * all of these were replaced to the IFilterState and IGridController */
            if (Controller != null)
            {
                Controller.LoadOrRefresh();
            }
            else
            {
                object dataSource;
                
                if (!binder.GetPropertyValue("DataSource", out dataSource))
                {
                    return; //No source sould be binded;
                }

                // DataSource = TranslateData(dataSource, DataValueField, DataTextField);

                DataSource = dataSource;

                DataBind();               
            }

            # region Selecting Items

            object selectedItems;

            if ((controller == null || String.IsNullOrEmpty(controller.SelectedItemsPath)) &&
               binder.GetPropertyValue("SelectedItems", out selectedItems) && selectedItems != null)
            {
                ICollection values = null;

                if (selectedItems is IDictionary)
                    values = (selectedItems as IDictionary).Keys;
                else
                    if (selectedItems is ICollection)
                        values = (selectedItems as ICollection);
                    else
                        if (selectedItems is StringDictionary)
                            values = (selectedItems as StringDictionary).Keys;
                        else
                            if (selectedItems is ListItemCollection)
                            {
                                values = new ArrayList();

                                foreach (ListItem item in (selectedItems as ListItemCollection))
                                {
                                    (values as IList).Add(item.Value);
                                }
                            }

                if (values == null)
                    throw new Exception("Unknown selected items type");


                foreach (object value in values)
                {
                    if (value == null)
                        throw new Exception("Selected item value can't be null");

                    ListItem item = Items.FindByValue((value is ListItem) ? (value as ListItem).Value : value.ToString());

                    if (item != null)
                        item.Selected = true;
                }
            }

            # endregion
        }

        private void Bind()
        {
            if (BeforeBinding != null)
                BeforeBinding(this);

            Visible = configuration.Visible;

            BindDataSource();

            binder.Bind(new string[] { "Enabled", "Visible" });

            if (controller == null || String.IsNullOrEmpty(controller.SelectedItemsPath))
            {
                binder.Bind("SelectedIndex");
                binder.Bind("SelectedValue");
            }

            Enabled = Enabled && configuration.IsEnabledForPrincipal();
            Visible = Visible && configuration.IsVisibleForPrincipal();

            if (AfterBinding != null)
                AfterBinding(this);
        }

        private void Unbind()
        {
            if (BeforeUnbinding != null)
                BeforeUnbinding(this);
            
            binder.Unbind(new string[] { "Enabled", "Visible", "SelectedIndex", "SelectedText", "SelectedValue", "DataSource", "DataValueField", "DataTextField" });

            //IList selectedItems = new ArrayList();
            IDictionary selectedItems = new OrderedDictionary();

            foreach (ListItem item in Items)
            {
                if (item.Selected)
                {
                    //selectedItems.Add(item);
                    selectedItems.Add(item.Value, item.Text);
                }
            }

            binder.SetPropertyValue("SelectedItems", selectedItems);

            if (AfterUnbinding != null)
                AfterUnbinding(this);
        }

        private IEnumerable TranslateData(object dataSource, string dataValueField, string dataTextField)
        {
            IEnumerable enumerable = ResolveDataSource(dataSource);
            if (enumerable == null)
            {
                return null;
            }
            DataTable dataTable = new DataTable();
            dataTable.Columns.Add("Text", typeof(object));
            dataTable.Columns.Add("Value", typeof(object));

            bool isExtractTextAndValue = (dataValueField != null) && (dataValueField.Length > 0) && (dataTextField != null) && (dataTextField.Length > 0);
            foreach (object item in enumerable)
            {
                DataRow dataRow = dataTable.NewRow();
                object text = isExtractTextAndValue ? System.Web.UI.DataBinder.GetPropertyValue(item, dataTextField) : item;
                dataRow["Text"] = Configuration.DataTranslator.Translate(this, text);
                dataRow["Value"] = isExtractTextAndValue ? System.Web.UI.DataBinder.GetPropertyValue(item, dataValueField) : item;
                dataTable.Rows.Add(dataRow);
            }
            return dataTable.DefaultView;
        }

        private IEnumerable ResolveDataSource(object dataSource)
        {
            if (dataSource == null)
            {
                return null;
            }
            IListSource listSource = dataSource as IListSource;
            if (listSource != null)
            {
                return listSource.GetList();
            }
            return dataSource as IEnumerable;
        }
    }
}
